#include "QD3DContext.h"

QD3DContext::QD3DContext (HWND hWnd)
{
	driverType = D3D_DRIVER_TYPE_NULL;
	featureLevel = D3D_FEATURE_LEVEL_11_0;
	pDevice = NULL;
	pImmediateContext = NULL;
	pSwapChain = NULL;
	pRenderTargetView = NULL;
	_initialize (hWnd);
}

QD3DContext::~QD3DContext ()
{
	_cleanup ();
}

LRESULT QD3DContext::_initialize (HWND hWnd)
{
	HRESULT hr = S_OK;

	RECT clientRect;
	GetClientRect (hWnd, &clientRect);
	UINT width = clientRect.right - clientRect.left;
	UINT height = clientRect.bottom - clientRect.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE (driverTypes);

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	UINT numFeatureLevels = ARRAYSIZE (featureLevels);

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory (&sd, sizeof(sd));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes;
		driverTypeIndex++) {
			driverType = driverTypes[driverTypeIndex];
			hr = D3D11CreateDeviceAndSwapChain (NULL, driverType, NULL,
				createDeviceFlags, featureLevels, numFeatureLevels,
				D3D11_SDK_VERSION, &sd,
				&pSwapChain, &pDevice, &featureLevel, &pImmediateContext);
			if (SUCCEEDED (hr))
				break;
	}
	if (FAILED (hr))
		return hr;

	// Create a render target view
	ID3D11Texture2D* pBackBuffer = NULL;
	hr = pSwapChain->GetBuffer (0, __uuidof(ID3D11Texture2D),
		(void**)&pBackBuffer);
	if (FAILED (hr))
		return hr;

	hr = pDevice->CreateRenderTargetView (pBackBuffer, NULL, &pRenderTargetView);
	pBackBuffer->Release ();
	if (FAILED (hr))
		return hr;

	// Create a depth stencil view
	ID3D11Texture2D* pDepthStencil = NULL;

	D3D11_TEXTURE2D_DESC descDepth;
	ZeroMemory (&descDepth, sizeof(descDepth));
	descDepth.Width = width;
	descDepth.Height = height;
	descDepth.MipLevels = 1;
	descDepth.ArraySize = 1;
	descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	descDepth.SampleDesc.Count = 1;
	descDepth.SampleDesc.Quality = 0;
	descDepth.Usage = D3D11_USAGE_DEFAULT;
	descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	descDepth.CPUAccessFlags = 0;
	descDepth.MiscFlags = 0;
	hr = pDevice->CreateTexture2D (&descDepth, NULL, &pDepthStencil);
	if (FAILED (hr))
		return hr;

	D3D11_DEPTH_STENCIL_VIEW_DESC DepthStencilViewDesc;
	ZeroMemory (&DepthStencilViewDesc, sizeof(DepthStencilViewDesc));
	DepthStencilViewDesc.Format = descDepth.Format;
	DepthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	DepthStencilViewDesc.Texture2D.MipSlice = 0;
	hr = pDevice->CreateDepthStencilView (pDepthStencil, &DepthStencilViewDesc,
			&pDepthStencilView);
	if (FAILED (hr))
		return hr;

	pImmediateContext->OMSetRenderTargets (1, &pRenderTargetView,
			pDepthStencilView);
	ID3D11RasterizerState *p_oldRasterizerState;
	CD3D11_RASTERIZER_DESC rasterizerStateDesc (D3D11_FILL_SOLID,
			D3D11_CULL_NONE, FALSE, 0, 0, 0, TRUE, FALSE, FALSE, FALSE);
	
	ID3D11RasterizerState *p_newRasterizerState;
	pDevice->CreateRasterizerState (&rasterizerStateDesc, &p_newRasterizerState);
	pImmediateContext->RSSetState (p_newRasterizerState);
	return 0;
}

void QD3DContext::_cleanup ()
{
// 	if (pImmediateContext) pImmediateContext->ClearState ();
// 	if (pRenderTargetView) pRenderTargetView->Release ();
// 	if (pSwapChain) pSwapChain->Release ();
// 	if (pImmediateContext) pImmediateContext->Release ();
// 	if (pDevice) pDevice->Release ();
	if (pSwapChain) pSwapChain->Release (); pSwapChain = NULL;
	if (pRenderTargetView) pRenderTargetView->Release (); pRenderTargetView = NULL;
	if (pDepthStencilView) pDepthStencilView->Release (); pDepthStencilView = NULL;
	if (pDevice) pDevice->Release (); pDevice = NULL;
	if (pImmediateContext) {
		pImmediateContext->Release ();
		pImmediateContext->ClearState ();
		pImmediateContext->Flush ();
		// 		pImmediateContext = NULL;
	}

}

QD3DContext *QD3DContext::cur ()
{
	if (_pCurrent == NULL)
		throw std::exception ("QD3DContext::cur ():"
				"Current context does not exist.");
	return _pCurrent;
}

void QD3DContext::lockCur (QD3DContext *pContext)
{
	if (_pCurrent != NULL && pContext != _pCurrent)
		throw std::exception ("QD3DContext::lockCur (): "
				"Cannot change current context because it's locked.");
	_pCurrent = pContext;
}

void QD3DContext::releaseCur (QD3DContext *pContext)
{
	if (_pCurrent != NULL && _pCurrent != pContext)
		throw std::exception ("QD3DContext::releaseCur (): "
				"Cannot release current context using another context");
	_pCurrent = NULL;
}

QD3DContext *QD3DContext::_pCurrent = NULL;